﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Threading;

using AForge;
using AForge.Controls;
using AForge.Imaging;
using AForge.Imaging.Filters;
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Video.VFW;
using AForge.Vision;
using AForge.Vision.Motion;

using NAudio;
using NAudio.Wave;

namespace WebcamMusic
{
    public partial class Form1 : Form
    {
        public string analysand;

        public bool displayMotion = true;

        public FilterInfoCollection usbWebcams;
        public VideoCaptureDevice camSource;
        public Bitmap image;

        public AVIFileVideoSource aviSource;
        public AVIReader reader;
        public AVIWriter tempWriter;
        public AVIWriter writer;

        public FileVideoSource fileVideoSource;
        public VideoSourcePlayer videoSourcePlayer;

        public MotionDetector detector;
        public MotionDetector detector1 = new MotionDetector(
            new TwoFramesDifferenceDetector(),
            new MotionBorderHighlighting());
        public MotionDetector detector2 = new MotionDetector(
            new TwoFramesDifferenceDetector(),
            new MotionBorderHighlighting());
        public MotionDetector detector3 = new MotionDetector(
            new TwoFramesDifferenceDetector(),
            new MotionBorderHighlighting());
        public MotionDetector detector4 = new MotionDetector(
            new TwoFramesDifferenceDetector(),
            new MotionBorderHighlighting());

        public List<float> history = new List<float>();

        public Rectangle[] zones = new Rectangle[4] {
            new Rectangle(0, 0, 100, 100),
            new Rectangle(110, 0, 100, 100),
            new Rectangle(0, 110, 100, 100),
            new Rectangle(110, 110, 100, 100) };

        public float motionAlarmLevel = 0.015f;
        public List<float> motionHistory = new List<float>();

        public DirectSoundOut output;
        public BlockAlignReductionStream stream;
        WaveTone tone = new WaveTone(440, 0.1);
        List<DirectSoundOut> outputs = new List<DirectSoundOut>();
        List<WaveChannel32> waves = new List<WaveChannel32>();
        List<EffectStream> effects = new List<EffectStream>();
        List<BlockAlignReductionStream> streams = new List<BlockAlignReductionStream>();

        public Form1()
        {
            InitializeComponent();

            detector1.MotionZones = new Rectangle[1] { new Rectangle(0, 0, 50, 50) };
            detector2.MotionZones = new Rectangle[1] { new Rectangle(270, 0, 50, 50) };
            detector3.MotionZones = new Rectangle[1] { new Rectangle(0, 190, 50, 50) };
            detector4.MotionZones = new Rectangle[1] { new Rectangle(270, 190, 50, 50) };
        }

        private void StreamReset(BlockAlignReductionStream stream)
        {
            if (stream.Position > 0)
            {
                stream.Position = 0;
            }
        }

        private void Play(DirectSoundOut sound, BlockAlignReductionStream stream)
        {
            try
            {
                sound.Init(stream);
                sound.Play();
                //StreamReset(stream);
                stream.Position = 0;
            }
            catch (ThreadStateException e) { }
        }

        private void Motion(Bitmap inputImage)
        {
            motionAlarmLevel = 0.002f;
            float motionLevel = 0;

            float motionLevel1 = detector1.ProcessFrame(inputImage);
            float motionLevel2 = detector2.ProcessFrame(inputImage);
            float motionLevel3 = detector3.ProcessFrame(inputImage);
            float motionLevel4 = detector4.ProcessFrame(inputImage);

            Thread first = new Thread(() => Play(outputs[0], streams[0]));
            Thread second = new Thread(() => Play(outputs[1], streams[1]));
            Thread third = new Thread(() => Play(outputs[2], streams[2]));
            Thread fourth = new Thread(() => Play(outputs[3], streams[3]));

            if (motionLevel1 > motionAlarmLevel)
            {
                first.Start();
            }

            if (motionLevel2 > motionAlarmLevel)
            {
                second.Start();
            }

            if (motionLevel3 > motionAlarmLevel)
            {
                third.Start();
            }

            if (motionLevel4 > motionAlarmLevel)
            {
                fourth.Start();
            }

            pictureBox1.Image = inputImage;
        }

        private void cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            image = (Bitmap)eventArgs.Frame.Clone();
            Thread motionThread = new Thread(() => Motion(image));
            motionThread.Start();

            //Bitmap newBitmap = image;
            //pictureBox1.Image = newBitmap;
        }

        private void webcamToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            CamSelector selector = new CamSelector();

            try
            {
                selector.ShowDialog();

                usbWebcams = selector.usbWebcams;
                camSource = selector.camSource;

                analysand = "camera";
            }
            catch { }
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void soundsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            openFileDialog1.Multiselect = true;
            openFileDialog1.Filter = "WAV files | *.wav;*.WAV;";
            DialogResult dr = openFileDialog1.ShowDialog();

            if (dr == DialogResult.OK)
            {
                if (openFileDialog1.FileNames.Length < 4)
                {
                    MessageBox.Show("At least four audio samples must be selected.");
                    soundsToolStripMenuItem_Click(sender, e);
                }
                else
                {
                    int i = 0;
                    foreach (String file in openFileDialog1.FileNames)
                    {
                        try
                        {
                            waves.Add(new WaveChannel32(new WaveFileReader(file)));
                            effects.Add(new EffectStream(waves[i]));
                            streams.Add(new BlockAlignReductionStream(effects[i]));
                            outputs.Add(new DirectSoundOut());
                            outputs[i].Init(streams[i]);
                        }
                        catch { }
                        i += 1;
                    }
                }
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void playButton_Click(object sender, EventArgs e)
        {
            detector = new MotionDetector(
                new TwoFramesDifferenceDetector(),
                new MotionBorderHighlighting());
            detector.MotionZones = zones;

            if (analysand != null)
            {
                stopButton_Click(sender, e);
            }

            if (analysand == "camera")
            {
                camSource.NewFrame += new NewFrameEventHandler(cam_NewFrame);
                camSource.Start();
            }
        }

        private void stopButton_Click(object sender, EventArgs e)
        {
            if (analysand == "camera" && camSource.IsRunning)
            {
                camSource.Stop();

                for (int i = 0; i < outputs.Count; i++)
                {
                    outputs[i].Stop();

                    StreamReset(streams[i]);
                }
            }
        }
    }

    public class WaveTone : WaveStream
    {
        public double frequency;
        public double amplitude;
        public double time;

        public WaveTone(double f, double a)
        {
            time = 0;
            frequency = f;
            amplitude = a;
        }

        public override long Position
        {
            get;
            set;
        }

        public override long Length
        {
            get { return long.MaxValue; }
        }

        public override WaveFormat WaveFormat
        {
            get { return new WaveFormat(44100, 16, 1); }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            int samples = count / 2;

            for (int i = 0; i < samples; i++)
            {
                double sine = amplitude * Math.Sin(Math.PI * 2 * frequency * time);
                time += 1.0 / 44100;
                short truncated = (short)Math.Round(sine * (Math.Pow(2, 15) - 1));
                buffer[i * 2] = (byte)(truncated & 0x00ff);
                buffer[i * 2 + 1] = (byte)((truncated & 0xff00) >> 8);
            }
            return count;
        }
    }
}
